package impls

import (
	"errors"
	"regexp"
	"strings"
	"time"

	"gitee.com/knowgo/gpa/engine/constants"
	"gitee.com/knowgo/gpa/engine/sql/dialects/dialect"
)

func RegisterMySQL(r dialect.Regitser) {
	r(constants.MYSQL, &mysqlDriver{
		baseDriver: baseDriver{fnMakeDialect: func() dialect.Dialect {
			return &postgres{}
		}},
	})
}
func RegisterMyMySQL(r dialect.Regitser) {
	r(constants.MYMYSQL, &mymysqlDriver{
		baseDriver: baseDriver{fnMakeDialect: func() dialect.Dialect {
			return &postgres{}
		}},
	})
}

type mymysqlDriver struct {
	baseDriver
}

func (p *mymysqlDriver) Parse(dialect constants.DIALECT, dataSourceName string) (*dialect.URI, error) {
	// uri := &intf.URI{DRIVER: constants.DB_MYSQL}
	uri, err := p.uri(dialect)
	if err != nil {
		return nil, err
	}
	pd := strings.SplitN(dataSourceName, "*", 2)
	if len(pd) == 2 {
		// Parse protocol part of URI
		p := strings.SplitN(pd[0], ":", 2)
		if len(p) != 2 {
			return nil, errors.New("wrong protocol part of URI")
		}
		uri.Proto = p[0]
		options := strings.Split(p[1], ",")
		uri.Raddr = options[0]
		for _, o := range options[1:] {
			kv := strings.SplitN(o, "=", 2)
			var k, v string
			if len(kv) == 2 {
				k, v = kv[0], kv[1]
			} else {
				k, v = o, "true"
			}
			switch k {
			case "laddr":
				uri.Laddr = v
			case "timeout":
				to, err := time.ParseDuration(v)
				if err != nil {
					return nil, err
				}
				uri.Timeout = to
			default:
				return nil, errors.New("Unknown option: " + k)
			}
		}
		// Remove protocol part
		pd = pd[1:]
	}
	// Parse database part of URI
	dup := strings.SplitN(pd[0], "/", 3)
	if len(dup) != 3 {
		return nil, errors.New("wrong database part of URI")
	}
	uri.DBName = dup[0]
	uri.User = dup[1]
	uri.Passwd = dup[2]

	return uri, nil
}

type mysqlDriver struct {
	baseDriver
}

func (p *mysqlDriver) Parse(dialect constants.DIALECT, dataSourceName string) (*dialect.URI, error) {
	dsnPattern := regexp.MustCompile(
		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
			`\/(?P<dbname>.*?)` + // /dbname
			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
	matches := dsnPattern.FindStringSubmatch(dataSourceName)
	// tlsConfigRegister := make(map[string]*tls.Config)
	names := dsnPattern.SubexpNames()

	// uri := &intf.URI{DRIVER: constants.DB_MYSQL}
	uri, err := p.uri(dialect)
	if err != nil {
		return nil, err
	}

	for i, match := range matches {
		switch names[i] {
		case "dbname":
			uri.DBName = match
		case "params":
			if len(match) > 0 {
				kvs := strings.Split(match, "&")
				for _, kv := range kvs {
					splits := strings.Split(kv, "=")
					if len(splits) == 2 {
						switch splits[0] {
						case "charset":
							uri.Charset = splits[1]
						}
					}
				}
			}

		}
	}
	return uri, nil
}
